//------------------------------------------------------------------------------ 
//      Copyright (c) 2004-2009, Be Nanashi-San 
// 
//      Permission is hereby granted, free of charge, to any person obtaining a 
//      copy of this software and associated documentation files (the "Software"), 
//      to deal in the Software without restriction, including without limitation 
//      the rights to use, copy, modify, merge, publish, distribute, sublicense, 
//      and/or sell copies of the Software, and to permit persons to whom the 
//      Software is furnished to do so, subject to the following conditions: 
// 
//      The above copyright notice and this permission notice shall be included in 
//      all copies or substantial portions of the Software. 
// 
//      THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
//      IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
//      FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
//      AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
//      LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
//      FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
//      DEALINGS IN THE SOFTWARE. 
// 
//      File Name:   BoardListView.cpp
//      Author:      Be Nanashi-San 
//      Description: BBS Board list display class. 
//------------------------------------------------------------------------------

//#define DEBUG 1
#include <Alert.h>
#include <Debug.h>
#include <PopUpMenu.h>
#include <MenuItem.h>
#include <MenuField.h>
#include <Window.h>
#include <ScrollView.h>
#include <stdio.h>
#include <stdlib.h>
#include <LayoutBuilder.h>
#include <GroupView.h>

#include "2chPositive.h"
#include "BoardItem.h"
#include "BoardListView.h"
#include "BoardOutLineListView.h"
#include "messages.h"
#include "setting.h"
#include "BBSHandler.h"
static const unsigned int kNAMEBUFSIZ = 256;
static const unsigned int kBBSCHANGE = 'kBBS';

////////////////////////////////////////////////////////////////////////////////
// BoardListView
//
// 20061018:B_SINGLE_SELECTION_LIST削除
////////////////////////////////////////////////////////////////////////////////
BoardListView::BoardListView()
:BGroupView()
,fStartUrl(NULL)
{
	fStopSem = create_sem(1, "BoardListReader_sem");
	fTreadRunner = new ThreadRunner<BoardListView>;
	this->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
}
////////////////////////////////////////////////////////////////////////////////
// ~BoardListView
//
//
////////////////////////////////////////////////////////////////////////////////
BoardListView::~BoardListView()
{
	delete_sem(fStopSem);
	if (fStartUrl) free(fStartUrl);
	delete fTreadRunner;
}
////////////////////////////////////////////////////////////////////////////////
// AttachedToWindow
//
//
////////////////////////////////////////////////////////////////////////////////
void
BoardListView::AttachedToWindow() 
{ 
	fCureader = static_cast<a2chViewer *>(be_app)->CurReader();
	BLayoutBuilder::Group<>(this)
			.AddGroup(B_VERTICAL, 2.0f)
			.AddGroup(B_HORIZONTAL, 0.0f)
			.Add(this->MakeBBSMenu())
			.AddGlue()
			.End()
			.Add(this->MakeBoardView())
			.End();

	fTreadRunner->Init(this, fStopSem);
	this->SetBBS();
	//TODO:リスト作成に失敗したときはどうするか？（板一覧置き場の移動など）
} 
////////////////////////////////////////////////////////////////////////////////
// MakeBBSMenu
//
//
////////////////////////////////////////////////////////////////////////////////
BMenuField *
BoardListView::MakeBBSMenu()
{
	BPopUpMenu *aMenu = new BPopUpMenu("");
	static_cast<a2chViewer *>(be_app)->MakeReaderList(aMenu);
	aMenu->SetTargetForItems(this);
	return new BMenuField("", aMenu);
}
////////////////////////////////////////////////////////////////////////////////
// MakeBoardView
//
//
////////////////////////////////////////////////////////////////////////////////
BScrollView *
BoardListView::MakeBoardView()
{
	fOutLineView = new BoardOutLineListView();
	fOutLineView->SetInvocationMessage(new BMessage(kMSG_BOARDLIST_INVOCATION));
	fOutLineView->SetTarget(this);
	this->SetDisplayFont();
	return new BScrollView("BoardListScrollView", fOutLineView , B_FOLLOW_ALL_SIDES, false, true);
}
////////////////////////////////////////////////////////////////////////////////
// OpenBookmark
// 20060924:BoardOutLineListViewより移動。
//Launch("application/x-vnd.Noname-2chPositive")で起動し
//すぐにkMSG_BOOKMARKのBMessageを送りたいのですが
//BoardListViewのSetBBS()で時間がかかりすぎ、
//kMSG_BOOKMARKを受け取った時点でまだ登録されてないと
//スルーしてしまいます。
//の対策
////////////////////////////////////////////////////////////////////////////////
void
BoardListView::OpenBookmark(BMessage *message)
{
	const char *url = message->FindString("url");
	if (fStartUrl) free(fStartUrl);
	fStartUrl = strdup(url);
	
	if (!fTreadRunner->IsRunning()) {
		this->InvokeBookmark();
	}
}

////////////////////////////////////////////////////////////////////////////////
// InvokeBookmark
// Bookmarkからの板、スレの起動
////////////////////////////////////////////////////////////////////////////////
void
BoardListView::InvokeBookmark()
{
	if (!fStartUrl) return;

	char dat[256];
	char *host = strdup(fStartUrl);
	char *p = strrchr(host, '/'); //20060924
	dat[0] = 0;
	strcpy(dat, p + 1);
	*(p + 1) = '\0';
	int32 find = fOutLineView->FindBoardUri(host);
	free(host);
	
	if (0 <= find ){
		BMessage msg(kMSG_BOARDLIST_INVOCATION);
		if(dat[0]){
			msg.AddString("dat", dat);
		}
		BMessenger(fOutLineView).SendMessage(&msg);	
	}else{
		//20060924
		(new BAlert("","ブックマーク先がリンク切れのようです。","OK"))->Go();
	}
	
	if (fStartUrl){
		free(fStartUrl);
		fStartUrl = NULL;
	}
}
////////////////////////////////////////////////////////////////////////////////
//
//
////////////////////////////////////////////////////////////////////////////////
status_t
BoardListView::Run(void *_this)
{
	aThread<BoardListView> *reader = static_cast<aThread<BoardListView> *>(_this);
	
	if (!reader->Lock())
		return B_INTERRUPTED;

	BoardListView *view = reader->View();

	if(view->fTreadRunner->StopLoading()) goto done;

	if(view->fOutLineView->IsEmpty() == false){
		for (;;){
			if (view->fTreadRunner->StopLoading() || !view->Window()->Lock()) goto done;
			BoardItem *item = static_cast<BoardItem *>(view->fOutLineView->RemoveItem(static_cast<int32>(0)));
			view->Window()->Unlock();
			if (item == NULL) break;
			delete item;
		}
		
		if (view->fTreadRunner->StopLoading() || !view->Window()->Lock()) goto done;
		view->fOutLineView->MakeEmpty(); //必要？
		view->Window()->Unlock();
	}

	if(view->fTreadRunner->StopLoading()) goto done;
	
	(view->fCureader)->DrawBoardList(view);


done:
	reader->Unlock();
	view->Window()->Lock();
	view->InvokeBookmark();
	view->Window()->Unlock();

	return B_NO_ERROR;
}
////////////////////////////////////////////////////////////////////////////////
// SetBBS
// 対象BBSを切り替える。現在は2ch(index=0)に固定
// forceRemoteが真の時、ローカルは無視してネットから読込む。
////////////////////////////////////////////////////////////////////////////////
status_t
BoardListView::SetBBS (bool forceNetLoad)
{
	status_t sts = B_ERROR;
	
	//Netからの読み込み強制なければローカルからの読み込み。
	if (!forceNetLoad){
		sts = fCureader->LoadFromLocal();
	}
	
	//Netからの読み込み強制またはローカルからの読み込み失敗。
	if(sts != B_OK){
		sts = fCureader->LoadFromInet();
	}

	if(sts != B_OK) return B_ERROR;
	
	fTreadRunner->Run();

	return B_OK;
}
////////////////////////////////////////////////////////////////////////////////
// BBSChanged
////////////////////////////////////////////////////////////////////////////////
void
BoardListView::BBSChanged (BMessage * message)
{
	void *item;
	message->FindPointer("source", &item);
	static_cast<a2chViewer *>(be_app)->SetCurReader(static_cast<BMenuItem *>(item)->Label());
	if(fCureader == static_cast<a2chViewer *>(be_app)->CurReader()) return;

	fCureader->WriteExpanded(fOutLineView);	// 切替える前に保存する。

	fCureader = static_cast<a2chViewer *>(be_app)->CurReader();
	this->SetBBS();
}

////////////////////////////////////////////////////////////////////////////////
// SetDisplayFont
//
// 
////////////////////////////////////////////////////////////////////////////////
void
BoardListView::SetDisplayFont()
{
BFont DispFont;
	DispFont.SetFamilyAndFace(theSettings().GetString(DISPLAY_BOARD_FONT_NAME), B_REGULAR_FACE);
	DispFont.SetSize(atof(theSettings().GetString(DISPLAY_BOARD_FONT_SIZE)));
	fOutLineView->SetFont(&DispFont);
	fOutLineView->Invalidate();	 //todo
}
////////////////////////////////////////////////////////////////////////////////
// MessageReceived
//
// 20061018:kMSG_FIND削除。
////////////////////////////////////////////////////////////////////////////////
void
BoardListView::MessageReceived (BMessage * message)
{
	switch (message->what){
		// zeta 2 スレ@757のBe名無しさんより。
	
		case kMENU_BOARDLIST_REFREASH:
			//TODO: BBS切替に対応。
			this->SetBBS(true); // refresh from inet.
			break;
	
		case kMENU_BOARDLIST_EXPANDALL:
			fOutLineView->ExpandOrCollaspse(true);
			break;
	
		case kMENU_BOARDLIST_COLLAPSEALL:
			fOutLineView->ExpandOrCollaspse(false);
			break;

		case kBBSCHANGE:
			this->BBSChanged (message);
			break;

		case kUPDATEPREFS:
			if (message->HasString(DISPLAY_BOARD_FONT_NAME) ||
					message->HasString(DISPLAY_BOARD_FONT_SIZE))
					this->SetDisplayFont();
			break;

		case kMSG_BOARDLIST_INVOCATION:
			BMessenger(fOutLineView).SendMessage(message);	
			break;
		default:
			BView::MessageReceived (message);
			break;
	}
}
